// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <google/protobuf/compiler/cpp/generator.h>
#include <google/protobuf/compiler/java/generator.h>
#include <google/protobuf/compiler/java/kotlin_generator.h>
#include <google/protobuf/compiler/command_line_interface.h>
#include <google/protobuf/compiler/csharp/csharp_generator.h>
#include <google/protobuf/compiler/csharp_shioho/shioho_provider.h>
#include <google/protobuf/compiler/csharp_shioho/shioho_paser.h>
#include <google/protobuf/compiler/csharp_shioho/shioho_controller.h>
#include <google/protobuf/compiler/objectivec/objectivec_generator.h>
#include <google/protobuf/compiler/php/php_generator.h>
#include <google/protobuf/compiler/python/generator.h>
#include <google/protobuf/compiler/python/pyi_generator.h>
#include <google/protobuf/compiler/ruby/ruby_generator.h>

#include <google/protobuf/compiler/csharp_shioho/shioho_msg.cc>

#include <string>
#include <vector>
#include <filesystem>

// Must be included last.
#include <google/protobuf/port_def.inc>
using namespace std;
namespace fs = std::filesystem;

class ProtoInfo
{
public:
	string path;
	string name;
};

vector<ProtoInfo> proto_files;
vector<MsgInfo> vec;
string provider_out;
string paser_out;
string controller_out;
string shioho_namespace;
filesystem::path cheat_path;

void AddProtoInfo(vector<ProtoInfo>& files, ProtoInfo file)
{
	for (size_t i = 0, length = files.size(); i < length; i++)
	{
		ProtoInfo old = files[i];
		if (old.name == file.name)
		{
			cout << "\nError:  proto filename repeat!!\n"
				<< "	[FileName]:" << file.name << "\n\n"
				<< "	[OldPath]: " + old.path + "\n"
				<< "	[NewPath]: " + file.path + "\n"
				<< endl;

			system("pause");
			exit(0);
		}
	}
	files.push_back(file);
}

void CollectAllProto(vector<ProtoInfo>& files, string dir)
{
	for (auto& p : fs::recursive_directory_iterator(dir))
	{
		if (p.is_directory())
			continue;

		auto filepath = p.path();
		auto extension = filepath.extension();
		if (extension.generic_string() != ".proto")
			continue;

		auto filename = filepath.filename();
		ProtoInfo proto;
		proto.path = filepath.generic_string();
		proto.name = filename.generic_string();

		AddProtoInfo(files, proto);
	}
}

bool comp(const MsgInfo& a, const MsgInfo& b)
{
	return a.protocolID < b.protocolID;
}

void ProtobufMain(int argc, char* argv[])
{
	// tmp为具体proto路径
	// argv = "-I=${sourceFolder}/ tmp --csharp_out=${targetFolder} --csharp_opt=namespace_name=XXX --provider_out=${targetFolder} --paser_out=${targetFolder} --controller_out=${targetFolder}";

	string dir = argv[1];
	//-I=替换为空
	dir = dir.replace(0, 3, "");

	vector<ProtoInfo> proto_files;
	CollectAllProto(proto_files, dir);

	//初始化namespace
	string opt = argv[4];
	opt = opt.substr(13);
	string namespaceName = "namespace_name=";
	string::size_type respPos = opt.find(namespaceName);
	shioho_namespace = opt.substr(respPos + namespaceName.length());

	//去除--provider_out=
	provider_out = argv[5];
	provider_out = provider_out.substr(15);

	//去除--paser_out=
	paser_out = argv[6];
	paser_out = paser_out.substr(12);

	//去除--controller_out=
	controller_out = argv[7];
	controller_out = controller_out.substr(17);

	for (size_t i = 0, length = proto_files.size(); i < length; i++)
	{
		// proto 文件名
		argv[2] = proto_files[i].path.data();

		google::protobuf::compiler::CommandLineInterface cli;
		cli.AllowPlugins("protoc-");
		google::protobuf::compiler::csharp::Generator csharp_generator;
		cli.RegisterGenerator("--csharp_out", "--csharp_opt", &csharp_generator,
			"Generate C# source file.");

		int result = cli.Run(argc - 4, argv);

		if (result == 0)
		{
			cout << "[Proto Generated Successfully] " << argv[2] << endl;
		}
		else
		{
			cout << "Error: Proto Generated Failed!!  File: " << argv[2] << endl;
		}
	}
	cout << endl;

	//排序
	sort(vec.begin(), vec.end(), comp);

	// MsgProvider.cs
	string providerDir = ("--csharp_out=" + provider_out);
	argv[3] = providerDir.data();
	google::protobuf::compiler::CommandLineInterface cli_provider;
	cli_provider.AllowPlugins("protoc-");
	google::protobuf::compiler::shioho_provider::Generator shioho_provider;
	cli_provider.RegisterGenerator("--csharp_out", "--csharp_opt", &shioho_provider,
		"Generate Shioho Provider source file.");

	int result = cli_provider.Run(argc - 4, argv);
	if (result == 0)
	{
		cout << "[MsgProvider Generated Successfully] " << endl;
	}
	else
	{
		cout << "Error: MsgProvider Generated Failed!!" << endl;
	}

	// MsgPaser.cs
	string paserDir = ("--csharp_out=" + paser_out);
	argv[3] = paserDir.data();
	google::protobuf::compiler::CommandLineInterface cli_paser;
	cli_paser.AllowPlugins("protoc-");
	google::protobuf::compiler::shioho_paser::Generator shioho_paser;
	cli_paser.RegisterGenerator("--csharp_out", "--csharp_opt", &shioho_paser,
		"Generate Shioho Paser source file.");
	result = cli_paser.Run(argc - 4, argv);
	if (result == 0)
	{
		cout << "[Shioho Paser All Files Generated Successfully] " << endl;
	}
	else
	{
		cout << "Error: Shioho Paser File Gnerated Failed!!" << endl;
	}

	// MsgController.cs
	string controllerDir = ("--csharp_out=" + controller_out);
	argv[3] = controllerDir.data();
	google::protobuf::compiler::CommandLineInterface cli_controller;
	cli_controller.AllowPlugins("protoc-");
	google::protobuf::compiler::shioho_controller::Generator shioho_controller;
	cli_controller.RegisterGenerator("--csharp_out", "--csharp_opt", &shioho_controller,
		"Generate Shioho Paser source file.");
	result = cli_controller.Run(argc - 4, argv);
	if (result == 0)
	{
		cout << "[Shioho Controller All Files Generated Successfully] " << endl;
	}
	else
	{
		cout << "Error: Shioho Controller File Gnerated Failed!!" << endl;
	}
}

void test(char* argv[])
{
	int argc = 8;
	char* arr[8];
	arr[0] = argv[0];
	arr[1] = "-I=F://ShiohoGit/unity-protobuf/MyProtoGen/Protos/";
	arr[2] = "tmp";
	arr[3] = "--csharp_out=F://ShiohoGit/unity-protobuf/MyProtoGen/Proto2CSharp/";
	arr[4] = "--csharp_opt=namespace_name=ShiohoNet";
	arr[5] = "--provider_out=F://ShiohoGit/unity-protobuf/MyProtoGen/Proto2CSharp/";
	arr[6] = "--paser_out=F://ShiohoGit/unity-protobuf/MyProtoGen/Proto2CSharp/";
	arr[7] = "--controller_out=F://ShiohoGit/unity-protobuf/MyProtoGen/Proto2CSharp/";
	ProtobufMain(argc, arr);
}

int main(int argc, char* argv[])
{
	//  test(argv);

	ProtobufMain(argc, argv);
	return 0;
}
